package com.container.core;

import com.container.util.MyEventExecutorGroup;
import com.google.common.base.StandardSystemProperty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.util.concurrent.AbstractEventExecutor;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;

import java.net.InetSocketAddress;

import static com.google.common.base.Preconditions.checkNotNull;

public class MyServletContainer implements EmbeddedServletContainer {
    private final Log log = LogFactory.getLog(getClass());
    private final InetSocketAddress address;
    private final MyServletContext servletContext;

    private EventLoopGroup bossGroup;//接收请求线程组
    private EventLoopGroup workerGroup;//处理请求线程组
    private AbstractEventExecutor servletExecutor;

    public MyServletContainer(InetSocketAddress address, MyServletContext servletContext) {
        this.address = address;
        this.servletContext = servletContext;
    }

    @Override
    public int getPort() {
        return address.getPort();
    }

    @Override
    public void start() throws EmbeddedServletContainerException {
        servletContext.setInitialized(false);//告诉上下文容器还未完成初始化
        ServerBootstrap sb = new ServerBootstrap();

        /*if ("Linux".equals(StandardSystemProperty.OS_NAME.value())) {//在linux系统下可以使用epoll
            bossGroup = new EpollEventLoopGroup(1);
            workerGroup = new EpollEventLoopGroup();//不传参，默认线程数为cpu*2
            sb.channel(EpollServerSocketChannel.class).group(bossGroup, workerGroup).option(EpollChannelOption.TCP_CORK, true);
        } else {//其他系统下不能使用epoll*/
            bossGroup = new NioEventLoopGroup(1);
            workerGroup = new NioEventLoopGroup();
            sb.channel(NioServerSocketChannel.class).group(bossGroup, workerGroup);
        //}

        sb.option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_BACKLOG, 100);

        servletExecutor = new MyEventExecutorGroup();//设置工作线程池
        
        sb.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel sc) throws Exception {
                ChannelPipeline cp = sc.pipeline();
                cp.addLast("codec", new HttpServerCodec(4096, 8192, 8192, false)); //直接使用netty自带的HTTP编解码器
                cp.addLast("servletInput", new ChannelReadHandler(servletContext)); //读数据，生成Request和Response对象
                cp.addLast(checkNotNull(servletExecutor), "filterChain", new RequestDispatcherHandler(servletContext)); //将请求分发给Servlet处理，这一步在工作线程池中进行
            }
        });

        servletContext.setInitialized(true);//容器初始化完成

        ChannelFuture future = sb.bind(address).awaitUninterruptibly();//绑定端口，开始监听
        Throwable t = future.cause();
        if (t != null) {
            throw new EmbeddedServletContainerException("Could not start Netty server", t);
        }
        log.info(servletContext.getServerInfo() + " started on port: " + getPort());
    }

    @Override
    public void stop() throws EmbeddedServletContainerException {
    	//关闭所有线程
        try {
            if (bossGroup != null) {
                bossGroup.shutdownGracefully().await();
            }
            if (workerGroup != null) {
                workerGroup.shutdownGracefully().await();
            }
            if (servletExecutor != null) {
                servletExecutor.shutdownGracefully().await();
            }
        } catch (InterruptedException e) {
            throw new EmbeddedServletContainerException("Container stop interrupted", e);
        }
    }
}